package com.lx.util;

import com.lx.annotation.Note;
import lombok.SneakyThrows;

import java.lang.reflect.Constructor;
import java.lang.reflect.Field;

class ReflectUtil {
    @SneakyThrows
    public static Object getFieldValue(Object obj, Field field) {
        return LX.ifNotNull(field, f -> {
            f.setAccessible(true);
            return f.get(obj);
        });
    }

    @SneakyThrows
    public static Object getFieldValue(Object obj, String propertyName) {
        return LX.ifNotNull(getFieldIncludingParents(obj.getClass(),propertyName), field -> {
            field.setAccessible(true);
            return field.get(obj);
        });
    }

    @SneakyThrows
    public static void setFieldValue(Object obj, String propertyName, Object value) {
        LX.ifNotNull(getFieldIncludingParents(obj.getClass(),propertyName), field -> {
            field.setAccessible(true);
            field.set(obj, value);
        });
    }

    @SneakyThrows
    public static Class getFieldType(Object obj, String propertyName) {
        return LX.ifNotNull(getFieldIncludingParents(obj.getClass(),propertyName), field -> {
            return field.getType();
        });
    }

    @Note("获取成员变量 包括父类的变量")
    public static Field getFieldIncludingParents(Class<?> clazz, String fieldName) throws NoSuchFieldException {
        try {
            return clazz.getDeclaredField(fieldName);
        } catch (NoSuchFieldException e) {
            Class<?> superClass = clazz.getSuperclass();
            if (superClass != null) {
                return getFieldIncludingParents(superClass, fieldName);
            }
            throw e;
        }
    }

    @SneakyThrows
    public static <T> Constructor<T> getConstructor(Class<? extends T> cls, Class<?>... parameterTypes) {
        return (Constructor<T>) cls.getConstructor(parameterTypes);
    }

    @SneakyThrows
    public static <T> T newInstance(Class<? extends T> cls) {
        return cls.getConstructor().newInstance();
    }

    @SneakyThrows
    public static <T> T newInstance(Class<? extends T> cls, Object[] para, Class<?>... parameterTypes) {
        return cls.getConstructor(parameterTypes).newInstance(para);
    }

    @SneakyThrows
    public static <T> T invokeMethod(Object obj, String methodName, Object[] para, Class<?>... parameterTypes) {
        return (T) obj.getClass().getDeclaredMethod(methodName, parameterTypes).invoke(obj, para);
    }
}